/*
* License:  This  program  is  free  software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the  Free Software Foundation; either version 3 of the License, or (at your
* option)  any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*/

package pcgen.CharacterViewer.resources;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.json.JSONException;
import org.json.JSONObject;

import android.os.Parcel;
import android.os.Parcelable;

import pcgen.android.Logger;

public class GameResourceAttributeAttack extends GameResourceAttribute
{
	public GameResourceAttributeAttack()
	{	
		setType(Types.Attack);
	}
	
	public GameResourceAttributeAttack(Parcel in)
	{
		super(in);
		setType(Types.Attack);
	}
	
	public static final Parcelable.Creator<GameResourceAttributeAttack> CREATOR = new Parcelable.Creator<GameResourceAttributeAttack>() 
	{
		public GameResourceAttributeAttack createFromParcel(Parcel in) 
		{
		    return new GameResourceAttributeAttack(in);
		}
		
		public GameResourceAttributeAttack[] newArray(int size) 
		{
		    return new GameResourceAttributeAttack[size];
		}
	};
	
	public int getAttackSeparator()
	{
		return _attackSeparator;
	}
	
	public int getAttackType()
	{
		return _attackType;
	}
	
	public int getAttackTypeSecondary()
	{
		return _attackTypeSecondary;
	}
	
	public String getBaseAttackBonusIterative()
	{
		return interativeAttackBonus(0);
	}
	
	@Override
	public int getAbilityType()
	{
		if (isRanged() || isFinessable())
			return GameAbilities.Dexterity;
		
		return GameAbilities.Strength;
	}
	
	public String getDamage()
	{
		return getDamage(0, false, false);
	}
	
	public String getDamage(int modifier, boolean offhand, boolean twohanded)
	{
		String value = _damageDiceNumber + "d" + _damageDiceType;
		int additional = getDamageAdditional(offhand, twohanded) + modifier;
		if (additional > 0)
			value += "+" + additional;
		return value;
	}
	
	public int getDamageAdditional(boolean offhand, boolean twohanded)
	{
		int modifier = getAbilityModifier(GameAbilities.Strength);
		if ((getHands() == 1) && offhand)
			modifier = modifier / 2;
		if ((getHands() == 2) || ((getHands() == 1) && isLight() && twohanded))
			modifier = (int)Math.round(modifier * 1.5);
		return getMisc() + modifier;
	}
	
	public int getHands()
	{
		return _hands;
	}
	
	@Override
	public int getModifier()
	{
		int value = super.getModifier();
		value = checkCondition(value);
		return value;
	}
	
	@Override
	public int getModifierTotal()
	{
		return getModifier() + getAbility().getModifier();
	}
	
	@Override
	public int getValue()
	{
		return getValueBase() + getValueAdditional();
	}
	
	public int getValueAdditional()
	{
		return getCharacter().getSizeBonus() + getMisc() + getAbilityModifier() + getModifier();
	}
	
	public String getValueIterative()
	{
		return interativeAttackBonus(getValueAdditional());
	}
	
	protected String interativeAttackBonus(int additional)
	{
		StringBuffer output = new StringBuffer();
		
		try
		{
			ArrayList<Integer> values = new ArrayList<Integer>();
			
			int attackSeparator = 5;
			if (_attackSeparator > 0)
				attackSeparator = _attackSeparator;
			
			// Determine iterative based on base value
			int temp = getCharacter().getAttackBase();
			while(temp > 0)
			{
				values.add(temp);
				temp -= attackSeparator;
			}
			
			for (Integer value : values)
			{
				value = checkCondition(value + additional);
				output.append(output.length() > 0 ? "/" : "").append("+").append(String.valueOf(value));
			}
		}
    	catch (Throwable tr)
    	{
    		Logger.e(TAG, "interativeAttackBonus", tr);
    	}
		
		return output.toString();
	}
	
	public boolean isBoth()
	{
		return isMelee() || isRanged();
	}
	
	public boolean isFinessable()
	{
		return _finessable;
	}
	
	public boolean isLight()
	{
		return isUnarmed();
	}
	
	public boolean isMelee()
	{
		return (_attackType == ATTACK_TYPE_MELEE) || (_attackType == ATTACK_TYPE_BOTH);
	}
	
	public boolean isRanged()
	{
		return (_attackType == ATTACK_TYPE_RANGED) || (_attackType == ATTACK_TYPE_BOTH);
	}
	
	public boolean isUnarmed()
	{
		return isMelee() && (_attackTypeSecondary == ATTACK_TYPE_SECONDARY_UNARMED);
	}
	
	@Override
	public JSONObject load(JSONObject data)
		throws JSONException
	{
		super.load(data);
		
		if (data == null)
			return null;

		setAttackType(getJSONInt(data, "attackType"));
		setAttackTypeSecondary(getJSONInt(data, "attackTypeSecondary"));
		setAttackSeparator(getJSONInt(data, "attackSeparator"));
		setHands(getJSONInt(data, "hands"));
		setFinessable(getJSONInt(data, "finessable") > 0 ? true : false);
		_damageDiceNumber = getJSONInt(data, "damageDiceNumber");
		_damageDiceType = getJSONInt(data, "damageDiceType");
	    
	    return data;
	}
	
	@Override
	public JSONObject save() 
		throws JSONException
	{
	    JSONObject data = super.save();

	    data.put("attackType", _attackType);
	    data.put("attackTypeSecondary", _attackTypeSecondary);
	    data.put("attackSeparator", _attackSeparator);
	    data.put("hands", _hands);
	    data.put("finessable", _finessable);
	    data.put("damageDiceNumber", _damageDiceNumber);
	    data.put("damageDiceType", _damageDiceType);
	    
	    return data;
	}
	
	@Override
	public void setAbilityType(int value)
	{
	}
	
	public void setAttackSeparator(int value)
	{
		_attackSeparator = value;
	}
	
	public void setAttackType(int value)
	{
		_attackType = value;
	}
	
	public void setAttackTypeSecondary(int value)
	{
		_attackTypeSecondary = value;
	}
	
	public void setDamage(String value)
	{
		_damageDiceNumber = 0;
		_damageDiceType = 0;
		Pattern doh = Pattern.compile("(\\d*)d(\\d*)", Pattern.DOTALL);
		Matcher matcher = doh.matcher(value); 
		if (matcher.find())
		{
			_damageDiceNumber = Integer.parseInt(matcher.group(1));
			_damageDiceType = Integer.parseInt(matcher.group(2));
		}
	}
	
	public void setFinessable(boolean value)
	{
		_finessable = value;
	}
	
	public void setHands(int value)
	{
		_hands = value;
	}
	
	public void setValue(int value, int type, int typeSecondary)
	{
		setValue(value, -1, type, typeSecondary);
	}
	
	public void setValue(int value, int attackSeparator, int type, int typeSecondary)
	{
		setValue(value);
		_attackSeparator = attackSeparator;
		setAttackType(type);
		setAttackTypeSecondary(typeSecondary);
	}
	
	protected int checkCondition(int value)
	{
		return getFolder().checkConditionAttack(this, value);
	}

	@Override
	protected void readFromParcelTransformEx(Parcel in)
		throws Throwable
	{
		try
		{
			super.readFromParcelTransformEx(in);
			
			setAttackType(in.readInt());
			setAttackTypeSecondary(in.readInt());
			setAttackSeparator(in.readInt());
			setHands(in.readInt());
			setFinessable(in.readInt() > 0 ? true : false);
			_damageDiceNumber = in.readInt();
			_damageDiceType = in.readInt();
		}
    	catch (Throwable tr)
    	{
    		Logger.e(TAG, "readFromParcelTransformEx", tr);
    		throw tr;
    	}
	}

	@Override
	protected void writeToParcelTransformEx(Parcel dest, int flags)
		throws Throwable
	{
		try
		{
			super.writeToParcelTransformEx(dest, flags);

			dest.writeInt(_attackType);
			dest.writeInt(_attackTypeSecondary);
			dest.writeInt(_attackSeparator);
			dest.writeInt(_hands);
			dest.writeInt(_finessable ? 1 : 0);
			dest.writeInt(_damageDiceNumber);
			dest.writeInt(_damageDiceType);
		}
    	catch (Throwable tr)
    	{
    		Logger.e(TAG, "writeToParcelTransformEx", tr);
    		throw tr;
    	}
	}

	private int _damageDiceNumber;
	private int _damageDiceType;
	private int _attackType = ATTACK_TYPE_MELEE;
	private int _attackTypeSecondary = ATTACK_TYPE_SECONDARY_UNARMED;
	private int _attackSeparator = -1;
	private boolean _finessable;
	private int _hands = 1;
	
	public static final int ATTACK_TYPE_BOTH = 0;
	public static final int ATTACK_TYPE_MELEE = 1;
	public static final int ATTACK_TYPE_RANGED = 2;
	public static final int ATTACK_TYPE_SECONDARY_NONE = 0;
	public static final int ATTACK_TYPE_SECONDARY_UNARMED = 1;

	private static final String TAG = GameResourceAttributeAttack.class.getSimpleName();
}
